//go:build windows

package pcdisklib

import (
	"bufio"
	"encoding/json"
	"fmt"
	"github.com/StackExchange/wmi"
	"github.com/urfave/cli/v2"
	"os"
	"strings"
)

var (
	BuildTime string
	Version   = "1.0.0"
)

func Do() {
	app := &cli.App{
		Name:    "disk",
		Version: fmt.Sprintf("%s build:%s", Version, BuildTime),
		Usage:   "disk [global options] command [command options] ",
		UsageText: "eg: disk -f=true -type=physics ,获取全部物理磁盘信息 \n disk -f=true -type=logic  ,获取全部逻辑磁盘信息 \n" +
			" disk -f=true -type=physics -s ,获取全部物理磁盘序列号 \n disk -f=true -type=logic -s ,获取全部逻辑磁盘序列号 \n" +
			" -f=true 为可选参数，默认为true，标记是否需要过滤无效节点(没有大小的磁盘或者没有序列号的逻辑卷标)",
		Description: "通过 wmi 物理硬盘和逻辑磁盘信息",
		Flags: []cli.Flag{
			&cli.BoolFlag{
				Name:    "filtration",
				Usage:   "是否需要过滤无效或不可使用的节点(没有大小或者没有分配逻辑卷序号的节点)",
				Aliases: []string{"f"},
				Value:   true,
			},
			&cli.StringFlag{
				Name:        "type",
				DefaultText: "physics",
				Usage:       "获取磁盘信息，可选参数：physics、logic.物理磁盘(physics)或者逻辑磁盘(logic)",
				Aliases:     []string{"t"},
				Required:    true,
				Value:       "physics",
			},
			&cli.BoolFlag{
				Name:        "serial",
				DefaultText: "false",
				Usage:       "获取磁盘序列号信息",
				Aliases:     []string{"s"},
				Required:    false,
			},
		},
		Action: func(ctx *cli.Context) error {
			var (
				filtration bool
				typeStr    string
				serialStr  bool
			)
			filtration = ctx.Bool("filtration")
			typeStr = ctx.String("type")
			serialStr = ctx.IsSet("serial")
			switch typeStr {
			case "physics":
				d, err := GetPhysicsDiskInfo(filtration)
				if err != nil {
					return err
				}
				if serialStr {
					str := make([]string, len(d))
					for k, v := range d {
						str[k] = v.SerialNumber
					}
					bs, err := json.Marshal(str)
					if err != nil {
						return err
					}
					w := bufio.NewWriter(os.Stdout)
					w.WriteString(string(bs) + "\n")
					w.Flush()
					return nil
				} else {
					bs, err := json.Marshal(d)
					if err != nil {
						return err
					}
					w := bufio.NewWriter(os.Stdout)
					w.WriteString(string(bs) + "\n")
					w.Flush()
					return nil
				}
			case "logic":
				d, err := GetLogicDiskInfo(filtration)
				if err != nil {
					return err
				}
				if serialStr {
					str := make([]string, len(d))
					for k, v := range d {
						str[k] = v.VolumeSerialNumber
					}
					bs, err := json.Marshal(str)
					if err != nil {
						return cli.Exit("消息转换为json字符串时发生错误", 1)
					}
					w := bufio.NewWriter(os.Stdout)
					w.WriteString(string(bs) + "\n")
					w.Flush()
					return nil
				} else {
					bs, err := json.Marshal(d)
					if err != nil {
						return err
					}
					w := bufio.NewWriter(os.Stdout)
					w.WriteString(string(bs) + "\n")
					w.Flush()
					return nil
				}
			default:
				return cli.Exit("type 指定参数不正确", 1)
			}

		},
	}
	if err := app.Run(os.Args); err != nil {
		fmt.Printf("命令行调用错误：%s ，请查看帮助文档", err.Error())
	}
}

type PhysicsDiskInfo struct {
	BytesPerSector   uint32 // eg:512 ,在每个扇区的物理磁盘驱动器的字节数。
	Caption          string // eg:PCIe SSD ,对象的序列号
	Description      string // eg:磁盘驱动器 ,描述
	DeviceID         string // eg: \\.\PHYSICALDRIVE1 ,磁盘驱动器与系统中的其他设备的唯一标识符
	FirmwareRevision string // eg:E8FM11.6 ,修订制造商分配的磁盘驱动器固件。
	InterfaceType    string // eg:SCSI ,物理磁盘驱动器的类型 （IDE、sata）
	Manufacturer     string // eg:(标准磁盘驱动器) ,制造商名称
	MediaType        string // eg:Fixed hard disk media ,由该设备使用或访问的媒体类型。
	Model            string // eg；PCIe SSD ，磁盘驱动器的制造商的型号。
	Name             string // eg:\\.\PHYSICALDRIVE1 ,名字
	SerialNumber     string // eg:6479_A721_8068_6489. 由制造商分配的号来识别物理介质。
	Size             uint64 // eg:256052966400 ,磁盘大小 单位字节 / 1024*1024*1024 才能得到G，
	SystemName       string // eg：HCY ，系统名称
}

// GetPhysicsDiskInfo 获取磁盘物理挂载点信息，
// clear 是否需要过滤掉存储空间大小为0 的节点
func GetPhysicsDiskInfo(filtration bool) (data []PhysicsDiskInfo, err error) {
	err = wmi.Query("SELECT * FROM Win32_DiskDrive", &data)
	if err != nil {
		return nil, err
	}
	for index := range data {
		data[index].SerialNumber = strings.TrimSpace(data[index].SerialNumber)
		if filtration {
			if data[index].Size == 0 {
				data = append(data[:index], data[index+1:]...)
			}
		}
	}
	return data, nil
}

type LogicDiskInfo struct {
	Caption            string //  eg:C: ,标题，也就是盘符
	Description        string // eg:本地固定磁盘 ,描述
	DeviceID           string // eg:C: , 在系统中的唯一标识符
	DriveType          uint32 // eg:3 , 磁盘类型，0-未知；1-No Root Directory；2-移动磁盘；3-本地磁盘；4-网络驱动器；5-光盘；6-RAM盘
	FileSystem         string // eg:NTFS , 磁盘格式
	FreeSpace          uint64 // eg:88191721472,空闲空间
	Size               string // eg:255369482240 ,总空间
	SupportsDiskQuotas bool   // 是否支持磁盘配额
	VolumeName         string // eg: 软件，逻辑卷别名
	VolumeSerialNumber string // eg:2AF1A3B8 ,逻辑卷序列号，并非磁盘序列号。
}

// GetLogicDiskInfo 获取逻辑磁盘信息，filtration 是否需要过滤哪些没有分配逻辑卷序列号的盘符
func GetLogicDiskInfo(filtration bool) (data []LogicDiskInfo, err error) {
	err = wmi.Query("SELECT * FROM Win32_LogicalDisk", &data)
	if err != nil {
		return nil, err
	}
	for index := range data {
		if filtration {
			if data[index].VolumeSerialNumber == "" {
				data = append(data[:index], data[index+1:]...)
			}
		}

	}
	return data, nil
}
